library(tidyverse)
package ‘tibble’ was built under R version 3.5.2
library(ggthemes)
library(gridExtra)
library(ggpubr) # for ggarrange
library(langcog) # for multi_boot_standard
theme_set(theme_few())
# langcog package is installed by running
# install.packages("devtools")
# devtools::install_github("langcog/langcog")
# make a base plots
# for flipped coordinate system with prop correct on x
p1 <- ggplot() +
geom_hline(yintercept = 1/3, lty = 2) +
scale_y_continuous("Proportion Correct", limits = c(-0.05, 1.05)) +
theme(axis.title.y = element_blank(), axis.ticks.y = element_blank())
p2 <- p1 + theme(axis.text.y = element_blank())
Intro
These are some preliminary visualizations of the data.
phylo <- read_csv("../data/species_data.csv") %>%
select(species, species_formatted, clade, clade_formatted, phylo)
mp_data <- read.csv("../data/merged_data/01_manyprimates_pilot_merged_data_v2.csv",
stringsAsFactors = F) %>%
left_join(phylo, by = "species") %>%
mutate(species = species_formatted,
species = reorder(species, phylo),
clade = clade_formatted,
clade = fct_relevel(clade, "Lemur", "New World monkey", "Old World monkey", "Ape"),
delay = factor(delay),
site = fct_recode(site,
"Edinburgh Zoo" = "edinburgh",
"Kumamoto Sanctuary" = "kumamoto sanctuary",
"Wolfgang Köhler Primate\nResearch Center" = "Leipzig Zoo",
"Sweetwaters Chimpanzee \nSanctuary Group 1" = "sweetwaters",
"Sweetwaters Chimpanzee\nSanctuary Group 2" = "sweetwaters_group2",
"Language Research Center" = "LRC"
)
)
Overview by species
First an overview of the data, plotted by species and delay. Small, transparent dots represent aggregated data for each individual by delay. Open dots are the group mean for that delay. Error bars are 95% confidence intervals. Text labels are the sample size.
plot_individual <- mp_data %>%
group_by(phylo, clade, species, delay, subject_site) %>%
summarise(correct = mean(correct),
nr_trials = max(trial))
plot_group <- plot_individual %>%
multi_boot_standard(col = "correct")
|== | 3% ~2 m remaining
|==== | 6% ~48 s remaining
|============== | 17% ~14 s remaining
|====================== | 25% ~9 s remaining
|==================================== | 42% ~4 s remaining
|=================================================== | 58% ~2 s remaining
|=============================================================== | 72% ~1 s remaining
|====================================================================== | 81% ~1 s remaining
|================================================================================ | 92% ~0 s remaining
# get sample sizes (for use in plots)
ns <- mp_data %>%
group_by(clade, species) %>%
mutate(n = n_distinct(subject_site)) %>%
group_by(clade, species, site, n) %>%
summarise(n_by_site = n_distinct(subject_site))
p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = plot_individual, aes(size = nr_trials), width = .1, height = .015, alpha = .15) +
geom_point(data = plot_group, aes(y = mean), shape = 1, size = 3, stroke = 1.5) +
geom_linerange(data = plot_group, aes(y = NULL, ymin = ci_lower, ymax = ci_upper), lwd = 1.2) +
# geom_text(data = ns, aes(label = n), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(species ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180, hjust = 0)) +
scale_size_area("Number of Trials", max_size = 3, breaks = c(12, 24, 36)) +
scale_colour_solarized("Delay", breaks = c("short", "medium", "long")) +
coord_flip(ylim = 0:1)

ggsave("../graphs/02_01_overview.png", width = 4, height = 4.5, scale = 2)
ggsave("../graphs/Fig2.tiff", width = 4, height = 4.5, scale = 2, type = "cairo", compression = "lzw")
Overview by species across delays
plot_individual2 <- plot_individual %>%
group_by(clade, species, subject_site) %>%
summarise(correct = mean(correct))
plot_group2 <- plot_individual2 %>%
multi_boot_standard(col = "correct")
p1 + aes(x = fct_rev(species), y = correct, col = clade) +
geom_jitter(data = plot_individual2, width = .1, height = .015, alpha = .15, size = 3) +
geom_point(data = plot_group2, aes(y = mean), shape = 1, size = 3, stroke = 1.5) +
geom_linerange(data = plot_group2, aes(y = NULL, ymin = ci_lower, ymax = ci_upper), lwd = 1.2) +
geom_text(data = ns, aes(label = n), y = -.05, col = "black", size = 3) +
facet_grid(clade ~ ., scales = "free_y", space = "free_y") +
theme(strip.text.y = element_blank()) +
scale_colour_solarized() +
coord_flip()

ggsave("../graphs/02_02_overview_across_delays.png", width = 4, height = 1.8, scale = 2)
Plots by site
Here we select the species for which we have data from multiple sites. This is a very preliminary way of checking whether there is a lot of variation between sites. Plotting conventions are the same as above.
First we check for which species we have data from more than one site:
mp_data %>%
group_by(species) %>%
summarise(sites = n_distinct(site)) %>%
arrange(desc(sites)) %>%
knitr::kable()
| Chimpanzee |
5 |
| Ring-tailed lemur |
2 |
| Brown capuchin monkey |
2 |
| Bonobo |
2 |
| Gorilla |
2 |
| Black-and-white ruffed lemur |
1 |
| Black-faced spider monkey |
1 |
| Squirrel monkey |
1 |
| Rhesus macaque |
1 |
| Long-tailed macaque |
1 |
| Barbary macaque |
1 |
| Orangutan |
1 |
Chimpanzees
chimp_plot_individual <- mp_data %>%
filter(species == "Chimpanzee") %>%
group_by(site, delay, subject_site) %>%
summarise(correct = mean(correct))
chimp_plot_group <- chimp_plot_individual %>%
multi_boot_standard(col = "correct")
ch <- p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = chimp_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
geom_pointrange(data = chimp_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
geom_text(data = filter(ns, species == "Chimpanzee"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(site ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180)) +
scale_colour_solarized("Delay", breaks = c("short", "medium", "long")) +
ggtitle("Chimpanzees") +
coord_flip()
ch

Ring-tailed lemurs
rtlemur_plot_individual <- mp_data %>%
filter(species == "Ring-tailed lemur") %>%
group_by(site, delay, subject_site) %>%
summarise(correct = mean(correct))
rtlemur_plot_group <- rtlemur_plot_individual %>%
multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = rtlemur_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
geom_pointrange(data = rtlemur_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
geom_text(data = filter(ns, species == "Ring-tailed lemur"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(site ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180)) +
scale_colour_solarized(breaks = c("short", "medium", "long")) +
ggtitle("Ring-tailed lemurs") +
coord_flip()

ggsave("../graphs/02_04_rtlemur_by_site.png", width = 4, height = 1.2, scale = 2)
Brown Capuchins
cap_plot_individual <- mp_data %>%
filter(species == "Brown capuchin monkey") %>%
group_by(site, delay, subject_site) %>%
summarise(correct = mean(correct))
cap_plot_group <- cap_plot_individual %>%
multi_boot_standard(col = "correct")
cap <- p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = cap_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
geom_pointrange(data = cap_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
geom_text(data = filter(ns, species == "Brown capuchin monkey"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(site ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180)) +
scale_colour_solarized("Delay", breaks = c("short", "medium", "long")) +
ggtitle("Capuchin Monkeys") +
coord_flip()
lay <- matrix(c(1, 1, 2))
grid.arrange(ch, cap, layout_matrix = lay)

ggsave("../graphs/02_05_chimp_capuchin_by_site.png", arrangeGrob(ch, cap, layout_matrix = lay), width = 4, height = 4, scale = 2)
ggsave("../graphs/Fig5.tiff", arrangeGrob(ch, cap, layout_matrix = lay), width = 4, height = 4, scale = 2, type = "cairo", compression = "lzw")
Bonobos
bon_plot_individual <- mp_data %>%
filter(species == "Bonobo") %>%
group_by(site, delay, subject_site) %>%
summarise(correct = mean(correct))
bon_plot_group <- bon_plot_individual %>%
multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = bon_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
geom_pointrange(data = bon_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
geom_text(data = filter(ns, species == "Bonobo"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(site ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180)) +
scale_colour_solarized(breaks = c("short", "medium", "long")) +
ggtitle("Bonobos") +
coord_flip()

ggsave("../graphs/02_06_bonobo_by_site.png", width = 4, height = 1.2, scale = 2)
Gorilla
gor_plot_individual <- mp_data %>%
filter(species == "Gorilla") %>%
group_by(site, delay, subject_site) %>%
summarise(correct = mean(correct))
gor_plot_group <- gor_plot_individual %>%
multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
geom_jitter(data = gor_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
geom_pointrange(data = gor_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
geom_text(data = filter(ns, species == "Gorilla"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(site ~ ., switch = "y") +
theme(strip.text.y = element_text(angle = 180)) +
scale_colour_solarized(breaks = c("short", "medium", "long")) +
ggtitle("Gorillas") +
coord_flip()

ggsave("../graphs/02_07_gorilla_by_site.png", width = 4, height = 1.2, scale = 2)
Task-experience
Here we split each species by task experience. Check if we have species with sufficient memebers having different levels of task experience.
mp_data %>%
group_by(species) %>%
mutate(lvls_task_exp = n_distinct(task_experience)) %>%
filter(lvls_task_exp > 1) %>%
group_by(species, task_experience) %>%
summarise(n = n_distinct(subject_site)) %>%
knitr::kable()
| Chimpanzee |
no |
19 |
| Chimpanzee |
yes |
32 |
| Bonobo |
no |
6 |
| Bonobo |
yes |
5 |
# get sample sizes (for use in plots)
ns_task_exp <- mp_data %>%
group_by(clade, species) %>%
mutate(n = n_distinct(task_experience)) %>%
group_by(clade, species, task_experience, n) %>%
summarise(n_by_site = n_distinct(subject_site))
So far, this only applies to chimps and bonobos. However, task experience co-varies with site.
chimp_task_plot_individual <- mp_data %>%
filter(species == "Chimpanzee") %>%
group_by(site, task_experience, delay, subject_site) %>%
summarise(correct = mean(correct))
chimp_task_plot_group <- mp_data %>%
filter(species == "Chimpanzee") %>%
group_by(task_experience, delay, subject_site) %>%
summarise(correct = mean(correct)) %>%
multi_boot_standard(col = "correct")
p_taskexp_chimp <- p2 + aes(x = delay, y = correct) +
geom_jitter(data = chimp_task_plot_individual, aes(col = site), width = .3, height = .015, alpha = .5, size = 3) +
geom_pointrange(data = chimp_task_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper, shape = delay), size = .8, stroke = 1.5) +
geom_text(data = filter(ns_task_exp, species == "Chimpanzee"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(task_experience ~ ., switch = "y") +
theme(legend.box = "horizontal", strip.text.y = element_text(angle = 180)) +
scale_shape_manual(values = c(1, 2, 5), breaks = c("short", "medium", "long")) +
scale_colour_solarized() +
ggtitle("Chimpanzees") +
coord_flip()
bonobo_task_plot_individual <- mp_data %>%
filter(species == "Bonobo") %>%
group_by(site, task_experience, delay, subject_site) %>%
summarise(correct = mean(correct))
bonobo_task_plot_group <- mp_data %>%
filter(species == "Bonobo") %>%
group_by(task_experience, delay, subject_site) %>%
summarise(correct = mean(correct)) %>%
multi_boot_standard(col = "correct")
p_taskexp_bon <- p2 + aes(x = delay, y = correct) +
geom_jitter(data = bonobo_task_plot_individual, aes(col = site), width = .3, height = .015, alpha = .5, size = 3) +
geom_pointrange(data = bonobo_task_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper, shape = delay), size = .8, stroke = 1.5) +
geom_text(data = filter(ns_task_exp, species == "Bbonobo"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
facet_grid(task_experience ~ ., switch = "y") +
theme(legend.box = "horizontal", strip.text.y = element_text(angle = 180)) +
scale_shape_manual(values = c(1, 2, 5), breaks = c("short", "medium", "long")) +
scale_colour_solarized() +
ggtitle("Bonobos") +
coord_flip()
grid.arrange(p_taskexp_chimp, p_taskexp_bon, ncol = 1)

grob <- arrangeGrob(p_taskexp_chimp, p_taskexp_bon, ncol = 1)
ggsave("../graphs/02_08_task_experience.png", grob, width = 4, height = 3, scale = 2)
Age
Here we plot age against correct choice separate for each delay and species. Regression line is smoothed delayal mean.
plot_age <- mp_data %>%
mutate(delay = fct_rev(delay)) %>%
group_by(subject_site, norm_age, clade, species, delay) %>%
summarise(correct = mean(correct))
ggplot(plot_age, aes(x = norm_age, y = correct)) +
geom_jitter(aes(col = clade), width = .05, height = .05, alpha = .5, size = 2.5) +
geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
# geom_vline(xintercept = 0, lty = 2) +
geom_hline(yintercept = 1/3, lty = 2) +
facet_grid(~ delay) +
labs(x = "Normed Age (relative to species longevity)", y = "Proportion Correct") +
scale_color_solarized("Clade") +
ylim(c(-.05, 1.05))

ggsave("../graphs/02_09_age_by_delay.png", width = 4, height = 1.3, scale = 2)
ggsave("../graphs/Fig4.tiff", width = 4, height = 1.3, scale = 2, type = "cairo", compression = "lzw")
ggplot(plot_age, aes(x = norm_age, y = correct)) +
geom_jitter(aes(fill = species), width = .05, height = .05, alpha = .5, size = 2.5, shape = 21, stroke = 0) +
geom_smooth(aes(col = delay), method = "glm", method.args = list(family = "binomial"), show.legend = F) +
geom_vline(xintercept = 0, lty = 2) +
geom_hline(yintercept = 1/3, lty = 2) +
facet_grid(delay ~ clade, scales = "free_x") +
labs(x = "Normed Age (relative to species longevity)", y = "Proportion Correct") +
theme(legend.position = "bottom") +
scale_color_manual(values = rev(solarized_palette(3))) +
ylim(c(-.05, 1.05))

ggsave("../graphs/02_10_age_by_delay_species.png", width = 4, height = 3.4, scale = 2)
Cup distance and board size
(sig. effect in model)
plot_cup <- mp_data %>%
group_by(subject_site, cup_distance, clade, species, delay) %>%
summarise(correct = mean(correct))
cp <- ggplot(plot_cup, aes(x = cup_distance, y = correct)) +
geom_jitter(aes(col = clade), width = .5, height = .05, alpha = .5, size = 2) +
geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
labs(x = "Cup Distance in cm", y = "Proportion Correct") +
geom_hline(yintercept = 1/3, lty = 2) +
scale_color_solarized("Clade") +
ylim(c(-.05, 1.05)) +
ggtitle("A. Cup Distance")
plot_board <- mp_data %>%
group_by(subject_site, board_size, clade, species, delay) %>%
summarise(correct = mean(correct))
bp <- ggplot(plot_board, aes(x = board_size, y = correct)) +
geom_jitter(aes(col = clade), width = .5, height = .05, alpha = .5, size = 2) +
geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
labs(x = "Board Size in cm", y = "Proportion Correct") +
geom_hline(yintercept = 1/3, lty = 2) +
scale_color_solarized("Clade") +
ylim(c(-.05, 1.05)) +
ggtitle("B. Board Size")
pcb <- ggarrange(cp, bp, common.legend = T, legend = "right")
non-integer #successes in a binomial glm!non-integer #successes in a binomial glm!non-integer #successes in a binomial glm!
pcb

ggsave("../graphs/02_11_cup_distance_board_size.png", pcb, width = 6, height = 2.3, scale = 2)
ggsave("../graphs/FigS1.tiff", pcb, width = 6, height = 2.3, scale = 2, type = "cairo", compression = "lzw")
LS0tCnRpdGxlOiAiTWFueVByaW1hdGVzIHBsb3RzIgphdXRob3I6ICJNYW51ZWwgQm9obiIKZGF0ZTogIk9jdCAzMSAyMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY3NzOiBzdHlsZS5jc3MKICAgIHRoZW1lOiBwYXBlcgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3B1YnIpICMgZm9yIGdnYXJyYW5nZQpsaWJyYXJ5KGxhbmdjb2cpICMgZm9yIG11bHRpX2Jvb3Rfc3RhbmRhcmQKCnRoZW1lX3NldCh0aGVtZV9mZXcoKSkKCiMgbGFuZ2NvZyBwYWNrYWdlIGlzIGluc3RhbGxlZCBieSBydW5uaW5nCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibGFuZ2NvZy9sYW5nY29nIikKYGBgCgpgYGB7cn0KIyBtYWtlIGEgYmFzZSBwbG90cwojIGZvciBmbGlwcGVkIGNvb3JkaW5hdGUgc3lzdGVtIHdpdGggcHJvcCBjb3JyZWN0IG9uIHgKcDEgPC0gZ2dwbG90KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEvMywgbHR5ID0gMikgKwogIHNjYWxlX3lfY29udGludW91cygiUHJvcG9ydGlvbiBDb3JyZWN0IiwgbGltaXRzID0gYygtMC4wNSwgMS4wNSkpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkKCnAyIDwtIHAxICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyBJbnRybwoKVGhlc2UgYXJlIHNvbWUgcHJlbGltaW5hcnkgdmlzdWFsaXphdGlvbnMgb2YgdGhlIGRhdGEuCgpgYGB7ciBsb2FkaW5nIGRhdGEsIG1lc3NhZ2U9RkFMU0V9CnBoeWxvIDwtIHJlYWRfY3N2KCIuLi9kYXRhL3NwZWNpZXNfZGF0YS5jc3YiKSAlPiUgCiAgc2VsZWN0KHNwZWNpZXMsIHNwZWNpZXNfZm9ybWF0dGVkLCBjbGFkZSwgY2xhZGVfZm9ybWF0dGVkLCBwaHlsbykKCm1wX2RhdGEgPC0gcmVhZC5jc3YoIi4uL2RhdGEvbWVyZ2VkX2RhdGEvMDFfbWFueXByaW1hdGVzX3BpbG90X21lcmdlZF9kYXRhX3YyLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JQogIGxlZnRfam9pbihwaHlsbywgYnkgPSAic3BlY2llcyIpICU+JQogIG11dGF0ZShzcGVjaWVzID0gc3BlY2llc19mb3JtYXR0ZWQsCiAgICAgICAgIHNwZWNpZXMgPSByZW9yZGVyKHNwZWNpZXMsIHBoeWxvKSwKICAgICAgICAgY2xhZGUgPSBjbGFkZV9mb3JtYXR0ZWQsCiAgICAgICAgIGNsYWRlID0gZmN0X3JlbGV2ZWwoY2xhZGUsICJMZW11ciIsICJOZXcgV29ybGQgbW9ua2V5IiwgIk9sZCBXb3JsZCBtb25rZXkiLCAiQXBlIiksCiAgICAgICAgIGRlbGF5ID0gZmFjdG9yKGRlbGF5KSwKICAgICAgICAgc2l0ZSA9IGZjdF9yZWNvZGUoc2l0ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJFZGluYnVyZ2ggWm9vIiA9ICJlZGluYnVyZ2giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkt1bWFtb3RvIFNhbmN0dWFyeSIgPSAia3VtYW1vdG8gc2FuY3R1YXJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIldvbGZnYW5nIEvDtmhsZXIgUHJpbWF0ZVxuUmVzZWFyY2ggQ2VudGVyIiA9ICJMZWlwemlnIFpvbyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTd2VldHdhdGVycyBDaGltcGFuemVlICAgXG5TYW5jdHVhcnkgR3JvdXAgMSIgPSAic3dlZXR3YXRlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiU3dlZXR3YXRlcnMgQ2hpbXBhbnplZVxuU2FuY3R1YXJ5IEdyb3VwIDIiID0gInN3ZWV0d2F0ZXJzX2dyb3VwMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYW5ndWFnZSBSZXNlYXJjaCBDZW50ZXIiID0gIkxSQyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKQopCmBgYAoKIyBPdmVydmlldyBieSBzcGVjaWVzCgpGaXJzdCBhbiBvdmVydmlldyBvZiB0aGUgZGF0YSwgcGxvdHRlZCBieSBzcGVjaWVzIGFuZCBkZWxheS4gU21hbGwsIHRyYW5zcGFyZW50IGRvdHMgcmVwcmVzZW50IGFnZ3JlZ2F0ZWQgZGF0YSBmb3IgZWFjaCBpbmRpdmlkdWFsIGJ5IGRlbGF5LiBPcGVuIGRvdHMgYXJlIHRoZSBncm91cCBtZWFuIGZvciB0aGF0IGRlbGF5LiBFcnJvciBiYXJzIGFyZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMuIFRleHQgbGFiZWxzIGFyZSB0aGUgc2FtcGxlIHNpemUuCgpgYGB7ciBvdmVydmlldyBieSBzcGVjaWVzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBncm91cF9ieShwaHlsbywgY2xhZGUsIHNwZWNpZXMsIGRlbGF5LCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSwKICAgICAgICAgICAgbnJfdHJpYWxzID0gbWF4KHRyaWFsKSkKCnBsb3RfZ3JvdXAgPC0gcGxvdF9pbmRpdmlkdWFsICU+JQogICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7cn0KIyBnZXQgc2FtcGxlIHNpemVzIChmb3IgdXNlIGluIHBsb3RzKQpucyA8LSBtcF9kYXRhICU+JQogIGdyb3VwX2J5KGNsYWRlLCBzcGVjaWVzKSAlPiUKICBtdXRhdGUobiA9IG5fZGlzdGluY3Qoc3ViamVjdF9zaXRlKSkgJT4lCiAgZ3JvdXBfYnkoY2xhZGUsIHNwZWNpZXMsIHNpdGUsIG4pICU+JQogIHN1bW1hcmlzZShuX2J5X3NpdGUgPSBuX2Rpc3RpbmN0KHN1YmplY3Rfc2l0ZSkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQuNX0KcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCwgY29sID0gZGVsYXkpICsKICBnZW9tX2ppdHRlcihkYXRhID0gcGxvdF9pbmRpdmlkdWFsLCBhZXMoc2l6ZSA9IG5yX3RyaWFscyksIHdpZHRoID0gLjEsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjE1KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcGxvdF9ncm91cCwgYWVzKHkgPSBtZWFuKSwgc2hhcGUgPSAxLCBzaXplID0gMywgc3Ryb2tlID0gMS41KSArCiAgZ2VvbV9saW5lcmFuZ2UoZGF0YSA9IHBsb3RfZ3JvdXAsIGFlcyh5ID0gTlVMTCwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBsd2QgPSAxLjIpICsKICAjIGdlb21fdGV4dChkYXRhID0gbnMsIGFlcyhsYWJlbCA9IG4pLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChzcGVjaWVzIH4gLiwgc3dpdGNoID0gInkiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTgwLCBoanVzdCA9IDApKSArCiAgc2NhbGVfc2l6ZV9hcmVhKCJOdW1iZXIgb2YgVHJpYWxzIiwgbWF4X3NpemUgPSAzLCBicmVha3MgPSBjKDEyLCAyNCwgMzYpKSArCiAgc2NhbGVfY29sb3VyX3NvbGFyaXplZCgiRGVsYXkiLCBicmVha3MgPSBjKCJzaG9ydCIsICJtZWRpdW0iLCAibG9uZyIpKSArCiAgY29vcmRfZmxpcCh5bGltID0gMDoxKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wMV9vdmVydmlldy5wbmciLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQuNSwgc2NhbGUgPSAyKQpnZ3NhdmUoIi4uL2dyYXBocy9GaWcyLnRpZmYiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQuNSwgc2NhbGUgPSAyLCB0eXBlID0gImNhaXJvIiwgY29tcHJlc3Npb24gPSAibHp3IikKYGBgCgojIE92ZXJ2aWV3IGJ5IHNwZWNpZXMgYWNyb3NzIGRlbGF5cwoKYGBge3Igb3ZlcnZpZXcgYnkgc3BlY2llcyBhY3Jvc3MgZGVsYXlzLCB3YXJuaW5nPUZBTFNFfQpwbG90X2luZGl2aWR1YWwyIDwtIHBsb3RfaW5kaXZpZHVhbCAlPiUKICBncm91cF9ieShjbGFkZSwgc3BlY2llcywgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpwbG90X2dyb3VwMiA8LSBwbG90X2luZGl2aWR1YWwyICU+JQogICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS44fQpwMSArIGFlcyh4ID0gZmN0X3JldihzcGVjaWVzKSwgeSA9IGNvcnJlY3QsIGNvbCA9IGNsYWRlKSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IHBsb3RfaW5kaXZpZHVhbDIsIHdpZHRoID0gLjEsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjE1LCBzaXplID0gMykgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBsb3RfZ3JvdXAyLCBhZXMoeSA9IG1lYW4pLCBzaGFwZSA9IDEsIHNpemUgPSAzLCBzdHJva2UgPSAxLjUpICsKICBnZW9tX2xpbmVyYW5nZShkYXRhID0gcGxvdF9ncm91cDIsIGFlcyh5ID0gTlVMTCwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBsd2QgPSAxLjIpICsKICBnZW9tX3RleHQoZGF0YSA9IG5zLCBhZXMobGFiZWwgPSBuKSwgeSA9IC0uMDUsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChjbGFkZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiLCBzcGFjZSA9ICJmcmVlX3kiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfY29sb3VyX3NvbGFyaXplZCgpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMDJfb3ZlcnZpZXdfYWNyb3NzX2RlbGF5cy5wbmciLCB3aWR0aCA9IDQsIGhlaWdodCA9IDEuOCwgc2NhbGUgPSAyKQpgYGAKCiMgUGxvdHMgYnkgc2l0ZQoKSGVyZSB3ZSBzZWxlY3QgdGhlIHNwZWNpZXMgZm9yIHdoaWNoIHdlIGhhdmUgZGF0YSBmcm9tIG11bHRpcGxlIHNpdGVzLiBUaGlzIGlzIGEgdmVyeSBwcmVsaW1pbmFyeSB3YXkgb2YgY2hlY2tpbmcgd2hldGhlciB0aGVyZSBpcyBhIGxvdCBvZiB2YXJpYXRpb24gYmV0d2VlbiBzaXRlcy4gUGxvdHRpbmcgY29udmVudGlvbnMgYXJlIHRoZSBzYW1lIGFzIGFib3ZlLgoKRmlyc3Qgd2UgY2hlY2sgZm9yIHdoaWNoIHNwZWNpZXMgd2UgaGF2ZSBkYXRhIGZyb20gbW9yZSB0aGFuIG9uZSBzaXRlOgoKYGBge3IgcGxvdHMgYnkgc2l0ZSwgcmVzdWx0cz0iYXNpcyJ9Cm1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lCiAgc3VtbWFyaXNlKHNpdGVzID0gbl9kaXN0aW5jdChzaXRlKSkgJT4lCiAgYXJyYW5nZShkZXNjKHNpdGVzKSkgJT4lCiAga25pdHI6OmthYmxlKCkKYGBgCgojIyBDaGltcGFuemVlcwoKYGBge3J9CmNoaW1wX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJDaGltcGFuemVlIikgJT4lCiAgZ3JvdXBfYnkoc2l0ZSwgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQoKY2hpbXBfcGxvdF9ncm91cCA8LSBjaGltcF9wbG90X2luZGl2aWR1YWwgJT4lCiAgIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0yLjV9CmNoIDwtIHAyICsgYWVzKHggPSBkZWxheSwgeSA9IGNvcnJlY3QsIGNvbCA9IGRlbGF5KSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGNoaW1wX3Bsb3RfaW5kaXZpZHVhbCwgd2lkdGggPSAuMSwgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuMywgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IGNoaW1wX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBzaXplID0gLjgsIHNoYXBlID0gMSwgc3Ryb2tlID0gMS41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIobnMsIHNwZWNpZXMgPT0gIkNoaW1wYW56ZWUiKSwgYWVzKGxhYmVsID0gbl9ieV9zaXRlKSwgeSA9IC0uMDUsIHggPSAyLCBjb2wgPSAiYmxhY2siLCBzaXplID0gMykgKwogIGZhY2V0X2dyaWQoc2l0ZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKCJEZWxheSIsIGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBnZ3RpdGxlKCJDaGltcGFuemVlcyIpICsKICBjb29yZF9mbGlwKCkKCmNoCmBgYAoKIyMgUmluZy10YWlsZWQgbGVtdXJzCgpgYGB7cn0KcnRsZW11cl9wbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiUmluZy10YWlsZWQgbGVtdXIiKSAlPiUKICBncm91cF9ieShzaXRlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpydGxlbXVyX3Bsb3RfZ3JvdXAgPC0gcnRsZW11cl9wbG90X2luZGl2aWR1YWwgJT4lCiAgIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjJ9CnAyICsgYWVzKHggPSBkZWxheSwgeSA9IGNvcnJlY3QsIGNvbCA9IGRlbGF5KSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IHJ0bGVtdXJfcGxvdF9pbmRpdmlkdWFsLCB3aWR0aCA9IC4xLCBoZWlnaHQgPSAuMDE1LCBhbHBoYSA9IC4zLCBzaXplID0gMykgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhID0gcnRsZW11cl9wbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4sIHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyKSwgc2l6ZSA9IC44LCBzaGFwZSA9IDEsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKG5zLCBzcGVjaWVzID09ICJSaW5nLXRhaWxlZCBsZW11ciIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChzaXRlIH4gLiwgc3dpdGNoID0gInkiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTgwKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIGdndGl0bGUoIlJpbmctdGFpbGVkIGxlbXVycyIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMDRfcnRsZW11cl9ieV9zaXRlLnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMS4yLCBzY2FsZSA9IDIpCmBgYAoKCiMjIEJyb3duIENhcHVjaGlucwoKYGBge3J9CmNhcF9wbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiQnJvd24gY2FwdWNoaW4gbW9ua2V5IikgJT4lCiAgZ3JvdXBfYnkoc2l0ZSwgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQoKY2FwX3Bsb3RfZ3JvdXAgPC0gY2FwX3Bsb3RfaW5kaXZpZHVhbCAlPiUKICAgbXVsdGlfYm9vdF9zdGFuZGFyZChjb2wgPSAiY29ycmVjdCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTEuMn0KY2FwIDwtIHAyICsgYWVzKHggPSBkZWxheSwgeSA9IGNvcnJlY3QsIGNvbCA9IGRlbGF5KSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGNhcF9wbG90X2luZGl2aWR1YWwsIHdpZHRoID0gLjEsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjMsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSBjYXBfcGxvdF9ncm91cCwgYWVzKHkgPSBtZWFuLCB5bWluID0gY2lfbG93ZXIsIHltYXggPSBjaV91cHBlciksIHNpemUgPSAuOCwgc2hhcGUgPSAxLCBzdHJva2UgPSAxLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihucywgc3BlY2llcyA9PSAiQnJvd24gY2FwdWNoaW4gbW9ua2V5IiksIGFlcyhsYWJlbCA9IG5fYnlfc2l0ZSksIHkgPSAtLjA1LCB4ID0gMiwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKHNpdGUgfiAuLCBzd2l0Y2ggPSAieSIpICsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxODApKSArCiAgc2NhbGVfY29sb3VyX3NvbGFyaXplZCgiRGVsYXkiLCBicmVha3MgPSBjKCJzaG9ydCIsICJtZWRpdW0iLCAibG9uZyIpKSArCiAgZ2d0aXRsZSgiQ2FwdWNoaW4gTW9ua2V5cyIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KbGF5IDwtIG1hdHJpeChjKDEsIDEsIDIpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00fQpncmlkLmFycmFuZ2UoY2gsIGNhcCwgbGF5b3V0X21hdHJpeCA9IGxheSkKYGBgCgpgYGB7cn0KZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMDVfY2hpbXBfY2FwdWNoaW5fYnlfc2l0ZS5wbmciLCBhcnJhbmdlR3JvYihjaCwgY2FwLCBsYXlvdXRfbWF0cml4ID0gbGF5KSwgd2lkdGggPSA0LCBoZWlnaHQgPSA0LCBzY2FsZSA9IDIpCmdnc2F2ZSgiLi4vZ3JhcGhzL0ZpZzUudGlmZiIsIGFycmFuZ2VHcm9iKGNoLCBjYXAsIGxheW91dF9tYXRyaXggPSBsYXkpLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQsIHNjYWxlID0gMiwgdHlwZSA9ICJjYWlybyIsIGNvbXByZXNzaW9uID0gImx6dyIpCmBgYAoKIyMgQm9ub2JvcwoKYGBge3J9CmJvbl9wbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiQm9ub2JvIikgJT4lCiAgZ3JvdXBfYnkoc2l0ZSwgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQoKYm9uX3Bsb3RfZ3JvdXAgPC0gYm9uX3Bsb3RfaW5kaXZpZHVhbCAlPiUKICAgbXVsdGlfYm9vdF9zdGFuZGFyZChjb2wgPSAiY29ycmVjdCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTEuMn0KcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCwgY29sID0gZGVsYXkpICsKICBnZW9tX2ppdHRlcihkYXRhID0gYm9uX3Bsb3RfaW5kaXZpZHVhbCwgd2lkdGggPSAuMSwgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuMywgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IGJvbl9wbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4sIHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyKSwgc2l6ZSA9IC44LCBzaGFwZSA9IDEsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKG5zLCBzcGVjaWVzID09ICJCb25vYm8iKSwgYWVzKGxhYmVsID0gbl9ieV9zaXRlKSwgeSA9IC0uMDUsIHggPSAyLCBjb2wgPSAiYmxhY2siLCBzaXplID0gMykgKwogIGZhY2V0X2dyaWQoc2l0ZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBnZ3RpdGxlKCJCb25vYm9zIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wNl9ib25vYm9fYnlfc2l0ZS5wbmciLCB3aWR0aCA9IDQsIGhlaWdodCA9IDEuMiwgc2NhbGUgPSAyKQpgYGAKCiMjIEdvcmlsbGEKCmBgYHtyfQpnb3JfcGxvdF9pbmRpdmlkdWFsIDwtIG1wX2RhdGEgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkdvcmlsbGEiKSAlPiUKICBncm91cF9ieShzaXRlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpnb3JfcGxvdF9ncm91cCA8LSBnb3JfcGxvdF9pbmRpdmlkdWFsICU+JQogICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4yfQpwMiArIGFlcyh4ID0gZGVsYXksIHkgPSBjb3JyZWN0LCBjb2wgPSBkZWxheSkgKwogIGdlb21faml0dGVyKGRhdGEgPSBnb3JfcGxvdF9pbmRpdmlkdWFsLCB3aWR0aCA9IC4xLCBoZWlnaHQgPSAuMDE1LCBhbHBoYSA9IC4zLCBzaXplID0gMykgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhID0gZ29yX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBzaXplID0gLjgsIHNoYXBlID0gMSwgc3Ryb2tlID0gMS41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIobnMsIHNwZWNpZXMgPT0gIkdvcmlsbGEiKSwgYWVzKGxhYmVsID0gbl9ieV9zaXRlKSwgeSA9IC0uMDUsIHggPSAyLCBjb2wgPSAiYmxhY2siLCBzaXplID0gMykgKwogIGZhY2V0X2dyaWQoc2l0ZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBnZ3RpdGxlKCJHb3JpbGxhcyIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMDdfZ29yaWxsYV9ieV9zaXRlLnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMS4yLCBzY2FsZSA9IDIpCmBgYAoKIyBUYXNrLWV4cGVyaWVuY2UKCkhlcmUgd2Ugc3BsaXQgZWFjaCBzcGVjaWVzIGJ5IHRhc2sgZXhwZXJpZW5jZS4gQ2hlY2sgaWYgd2UgaGF2ZSBzcGVjaWVzIHdpdGggc3VmZmljaWVudCBtZW1lYmVycyBoYXZpbmcgZGlmZmVyZW50IGxldmVscyBvZiB0YXNrIGV4cGVyaWVuY2UuCgpgYGB7ciBwbG90cyBieSB0YXNrIGV4cGVyaWVuY2UsIHJlc3VsdHM9ImFzaXMifQptcF9kYXRhICU+JQogIGdyb3VwX2J5KHNwZWNpZXMpICU+JQogIG11dGF0ZShsdmxzX3Rhc2tfZXhwID0gbl9kaXN0aW5jdCh0YXNrX2V4cGVyaWVuY2UpKSAlPiUKICBmaWx0ZXIobHZsc190YXNrX2V4cCA+IDEpICU+JQogIGdyb3VwX2J5KHNwZWNpZXMsIHRhc2tfZXhwZXJpZW5jZSkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuX2Rpc3RpbmN0KHN1YmplY3Rfc2l0ZSkpICU+JQogIGtuaXRyOjprYWJsZSgpCmBgYAoKYGBge3J9CiMgZ2V0IHNhbXBsZSBzaXplcyAoZm9yIHVzZSBpbiBwbG90cykKbnNfdGFza19leHAgPC0gbXBfZGF0YSAlPiUKICBncm91cF9ieShjbGFkZSwgc3BlY2llcykgJT4lCiAgbXV0YXRlKG4gPSBuX2Rpc3RpbmN0KHRhc2tfZXhwZXJpZW5jZSkpICU+JQogIGdyb3VwX2J5KGNsYWRlLCBzcGVjaWVzLCB0YXNrX2V4cGVyaWVuY2UsIG4pICU+JQogIHN1bW1hcmlzZShuX2J5X3NpdGUgPSBuX2Rpc3RpbmN0KHN1YmplY3Rfc2l0ZSkpCmBgYAoKU28gZmFyLCB0aGlzIG9ubHkgYXBwbGllcyB0byBjaGltcHMgYW5kIGJvbm9ib3MuIEhvd2V2ZXIsIHRhc2sgZXhwZXJpZW5jZSBjby12YXJpZXMgd2l0aCBzaXRlLgoKPCEtLSAjIyBDaGltcGFuemVlcyAtLT4KCmBgYHtyfQpjaGltcF90YXNrX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJDaGltcGFuemVlIikgJT4lCiAgZ3JvdXBfYnkoc2l0ZSwgdGFza19leHBlcmllbmNlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpjaGltcF90YXNrX3Bsb3RfZ3JvdXAgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiQ2hpbXBhbnplZSIpICU+JQogIGdyb3VwX2J5KHRhc2tfZXhwZXJpZW5jZSwgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKSAlPiUKICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4yfQpwX3Rhc2tleHBfY2hpbXAgPC0gcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCkgKwogIGdlb21faml0dGVyKGRhdGEgPSBjaGltcF90YXNrX3Bsb3RfaW5kaXZpZHVhbCwgYWVzKGNvbCA9IHNpdGUpLCB3aWR0aCA9IC4zLCBoZWlnaHQgPSAuMDE1LCBhbHBoYSA9IC41LCBzaXplID0gMykgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhID0gY2hpbXBfdGFza19wbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4sIHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyLCBzaGFwZSA9IGRlbGF5KSwgc2l6ZSA9IC44LCBzdHJva2UgPSAxLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihuc190YXNrX2V4cCwgc3BlY2llcyA9PSAiQ2hpbXBhbnplZSIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZCh0YXNrX2V4cGVyaWVuY2UgfiAuLCBzd2l0Y2ggPSAieSIpICsKICB0aGVtZShsZWdlbmQuYm94ID0gImhvcml6b250YWwiLCBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxODApKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgMiwgNSksIGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKCkgKwogIGdndGl0bGUoIkNoaW1wYW56ZWVzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCjwhLS0gIyMgQm9ub2JvcyAtLT4KCmBgYHtyfQpib25vYm9fdGFza19wbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiQm9ub2JvIikgJT4lCiAgZ3JvdXBfYnkoc2l0ZSwgdGFza19leHBlcmllbmNlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpib25vYm9fdGFza19wbG90X2dyb3VwIDwtIG1wX2RhdGEgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkJvbm9ibyIpICU+JQogIGdyb3VwX2J5KHRhc2tfZXhwZXJpZW5jZSwgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKSAlPiUKICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4yfQpwX3Rhc2tleHBfYm9uIDwtIHAyICsgYWVzKHggPSBkZWxheSwgeSA9IGNvcnJlY3QpICsKICBnZW9tX2ppdHRlcihkYXRhID0gYm9ub2JvX3Rhc2tfcGxvdF9pbmRpdmlkdWFsLCBhZXMoY29sID0gc2l0ZSksIHdpZHRoID0gLjMsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjUsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSBib25vYm9fdGFza19wbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4sIHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyLCBzaGFwZSA9IGRlbGF5KSwgc2l6ZSA9IC44LCBzdHJva2UgPSAxLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihuc190YXNrX2V4cCwgc3BlY2llcyA9PSAiQmJvbm9ibyIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZCh0YXNrX2V4cGVyaWVuY2UgfiAuLCBzd2l0Y2ggPSAieSIpICsKICB0aGVtZShsZWdlbmQuYm94ID0gImhvcml6b250YWwiLCBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxODApKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgMiwgNSksIGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKCkgKwogIGdndGl0bGUoIkJvbm9ib3MiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTN9CmdyaWQuYXJyYW5nZShwX3Rhc2tleHBfY2hpbXAsIHBfdGFza2V4cF9ib24sIG5jb2wgPSAxKQpgYGAKCmBgYHtyfQpncm9iIDwtIGFycmFuZ2VHcm9iKHBfdGFza2V4cF9jaGltcCwgcF90YXNrZXhwX2JvbiwgbmNvbCA9IDEpCmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzA4X3Rhc2tfZXhwZXJpZW5jZS5wbmciLCBncm9iLCB3aWR0aCA9IDQsIGhlaWdodCA9IDMsIHNjYWxlID0gMikKYGBgCgoKIyBBZ2UKCkhlcmUgd2UgcGxvdCBhZ2UgYWdhaW5zdCBjb3JyZWN0IGNob2ljZSBzZXBhcmF0ZSBmb3IgZWFjaCBkZWxheSBhbmQgc3BlY2llcy4gUmVncmVzc2lvbiBsaW5lIGlzIHNtb290aGVkIGRlbGF5YWwgbWVhbi4KCmBgYHtyfQpwbG90X2FnZSA8LSBtcF9kYXRhICU+JQogIG11dGF0ZShkZWxheSA9IGZjdF9yZXYoZGVsYXkpKSAlPiUKICBncm91cF9ieShzdWJqZWN0X3NpdGUsIG5vcm1fYWdlLCBjbGFkZSwgc3BlY2llcywgZGVsYXkpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4zfQpnZ3Bsb3QocGxvdF9hZ2UsIGFlcyh4ID0gbm9ybV9hZ2UsIHkgPSBjb3JyZWN0KSkgKwogIGdlb21faml0dGVyKGFlcyhjb2wgPSBjbGFkZSksIHdpZHRoID0gLjA1LCBoZWlnaHQgPSAuMDUsIGFscGhhID0gLjUsIHNpemUgPSAyLjUpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBjb2wgPSAiYmxhY2siKSArCiAgIyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS8zLCBsdHkgPSAyKSArCiAgZmFjZXRfZ3JpZCh+IGRlbGF5KSArCiAgbGFicyh4ID0gIk5vcm1lZCBBZ2UgKHJlbGF0aXZlIHRvIHNwZWNpZXMgbG9uZ2V2aXR5KSIsIHkgPSAiUHJvcG9ydGlvbiBDb3JyZWN0IikgKwogIHNjYWxlX2NvbG9yX3NvbGFyaXplZCgiQ2xhZGUiKSArCiAgeWxpbShjKC0uMDUsIDEuMDUpKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wOV9hZ2VfYnlfZGVsYXkucG5nIiwgd2lkdGggPSA0LCBoZWlnaHQgPSAxLjMsIHNjYWxlID0gMikKZ2dzYXZlKCIuLi9ncmFwaHMvRmlnNC50aWZmIiwgd2lkdGggPSA0LCBoZWlnaHQgPSAxLjMsIHNjYWxlID0gMiwgdHlwZSA9ICJjYWlybyIsIGNvbXByZXNzaW9uID0gImx6dyIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTMuNH0KZ2dwbG90KHBsb3RfYWdlLCBhZXMoeCA9IG5vcm1fYWdlLCB5ID0gY29ycmVjdCkpICsKICBnZW9tX2ppdHRlcihhZXMoZmlsbCA9IHNwZWNpZXMpLCB3aWR0aCA9IC4wNSwgaGVpZ2h0ID0gLjA1LCBhbHBoYSA9IC41LCBzaXplID0gMi41LCBzaGFwZSA9IDIxLCBzdHJva2UgPSAwKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbCA9IGRlbGF5KSwgbWV0aG9kID0gImdsbSIsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHkgPSAiYmlub21pYWwiKSwgc2hvdy5sZWdlbmQgPSBGKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEvMywgbHR5ID0gMikgKwogIGZhY2V0X2dyaWQoZGVsYXkgfiBjbGFkZSwgc2NhbGVzID0gImZyZWVfeCIpICsKICBsYWJzKHggPSAiTm9ybWVkIEFnZSAocmVsYXRpdmUgdG8gc3BlY2llcyBsb25nZXZpdHkpIiwgeSA9ICJQcm9wb3J0aW9uIENvcnJlY3QiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcmV2KHNvbGFyaXplZF9wYWxldHRlKDMpKSkgKwogIHlsaW0oYygtLjA1LCAxLjA1KSkKYGBgCgpgYGB7cn0KZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMTBfYWdlX2J5X2RlbGF5X3NwZWNpZXMucG5nIiwgd2lkdGggPSA0LCBoZWlnaHQgPSAzLjQsIHNjYWxlID0gMikKYGBgCgoKIyBDdXAgZGlzdGFuY2UgYW5kIGJvYXJkIHNpemUKCihzaWcuIGVmZmVjdCBpbiBtb2RlbCkKCmBgYHtyfQpwbG90X2N1cCA8LSBtcF9kYXRhICU+JQogICBncm91cF9ieShzdWJqZWN0X3NpdGUsIGN1cF9kaXN0YW5jZSwgY2xhZGUsIHNwZWNpZXMsIGRlbGF5KSAlPiUKICAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQpgYGAKCmBgYHtyIHBsb3R0aW5nIGN1cCBkaXN0YW5jZX0KY3AgPC0gZ2dwbG90KHBsb3RfY3VwLCBhZXMoeCA9IGN1cF9kaXN0YW5jZSwgeSA9IGNvcnJlY3QpKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbCA9IGNsYWRlKSwgd2lkdGggPSAuNSwgaGVpZ2h0ID0gLjA1LCBhbHBoYSA9IC41LCBzaXplID0gMikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIGNvbCA9ICJibGFjayIpICsKICBsYWJzKHggPSAiQ3VwIERpc3RhbmNlIGluIGNtIiwgeSA9ICJQcm9wb3J0aW9uIENvcnJlY3QiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS8zLCBsdHkgPSAyKSArCiAgc2NhbGVfY29sb3Jfc29sYXJpemVkKCJDbGFkZSIpICsKICB5bGltKGMoLS4wNSwgMS4wNSkpICsKICBnZ3RpdGxlKCJBLiBDdXAgRGlzdGFuY2UiKQpgYGAKCmBgYHtyfQpwbG90X2JvYXJkIDwtIG1wX2RhdGEgJT4lCiAgIGdyb3VwX2J5KHN1YmplY3Rfc2l0ZSwgYm9hcmRfc2l6ZSwgY2xhZGUsIHNwZWNpZXMsIGRlbGF5KSAlPiUKICAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQpgYGAKCmBgYHtyIHBsb3R0aW5nIGJvYXJkIHNpemV9CmJwIDwtIGdncGxvdChwbG90X2JvYXJkLCBhZXMoeCA9IGJvYXJkX3NpemUsIHkgPSBjb3JyZWN0KSkgKwogIGdlb21faml0dGVyKGFlcyhjb2wgPSBjbGFkZSksIHdpZHRoID0gLjUsIGhlaWdodCA9IC4wNSwgYWxwaGEgPSAuNSwgc2l6ZSA9IDIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBjb2wgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIkJvYXJkIFNpemUgaW4gY20iLCB5ID0gIlByb3BvcnRpb24gQ29ycmVjdCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLzMsIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl9zb2xhcml6ZWQoIkNsYWRlIikgKwogIHlsaW0oYygtLjA1LCAxLjA1KSkgKwogIGdndGl0bGUoIkIuIEJvYXJkIFNpemUiKQpgYGAKCmBgYHtyfQpwY2IgPC0gZ2dhcnJhbmdlKGNwLCBicCwgY29tbW9uLmxlZ2VuZCA9IFQsIGxlZ2VuZCA9ICJyaWdodCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTIuM30KcGNiCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzExX2N1cF9kaXN0YW5jZV9ib2FyZF9zaXplLnBuZyIsIHBjYiwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjMsIHNjYWxlID0gMikKZ2dzYXZlKCIuLi9ncmFwaHMvRmlnUzEudGlmZiIsIHBjYiwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjMsIHNjYWxlID0gMiwgdHlwZSA9ICJjYWlybyIsIGNvbXByZXNzaW9uID0gImx6dyIpCmBgYAoK